package com.ruoyi.system.service.impl;

import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.MaritimeConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.ConstantsEnum;
import com.ruoyi.common.enums.DeviceWarnStatus;
import com.ruoyi.common.enums.ProductEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.my.MyRedis;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanValidators;
import com.ruoyi.common.utils.modbus.Modbus4jWriteUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.utils.printer.PrintDemo;
import com.ruoyi.system.domain.MrDevice;
import com.ruoyi.system.domain.MrDeviceAlarmData;
import com.ruoyi.system.domain.vo.*;
import com.ruoyi.system.mapper.MrDeviceMapper;
import com.ruoyi.system.mapper.SysDictDataMapper;
import com.ruoyi.system.service.IMrDeviceAlarmDataService;
import com.ruoyi.system.service.IMrDeviceService;
import com.ruoyi.system.service.SensorDataService;
import com.ruoyi.system.utils.ClassMethodNameUtil;
import com.ruoyi.system.utils.LogUtil;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Validator;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 设备管理Service业务层处理
 *
 * @author ruoyi
 * @date 2022-11-18
 */
@Service
public class MrDeviceServiceImpl implements IMrDeviceService {
    private final Logger logger = LoggerFactory.getLogger(MrDeviceServiceImpl.class);

    @Autowired
    private MrDeviceMapper mrDeviceMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private SysDictDataMapper sysDictDataMapper;

    @Autowired
    protected Validator validator;

    @Autowired
    private IMrDeviceAlarmDataService mrDeviceAlarmDataService;

    @Autowired
    private SensorDataService sensorDataService;

    @Autowired
    private LogUtil logUtil;

    private static final String MODALITY_LIST = "modality_list";

    /**
     * 项目启动时，初始化到缓存
     */
    @PostConstruct
    public void init() {
        loadingCache();
    }

    @Override
    public void loadingCache() {
        MrDevice mrDevice = new MrDevice();
        List<MrDevice> deviceList = mrDeviceMapper.selectMrDeviceList(mrDevice);

        if (deviceList != null && !deviceList.isEmpty()) {
            for (MrDevice device : deviceList) {
                String label = sysDictDataMapper.selectDictLabel(MODALITY_LIST, device.getModality());
                device.setModality(label);
                redisCache.setCacheObject(CacheConstants.DEV_INFO_CACHE + device.getDeviceId(), device);
            }
        }
    }

    /**
     * 查询设备管理
     *
     * @param deviceId 设备管理主键
     * @return 设备管理
     */
    @Override
    public MrDevice selectMrDeviceByDeviceId(Long deviceId) {
        return mrDeviceMapper.selectMrDeviceByDeviceId(deviceId);
    }

    /**
     * 查询设备管理列表
     *
     * @param mrDevice 设备管理
     * @return 设备管理
     */
    @Override
    public List<MrDevice> selectMrDeviceList(MrDevice mrDevice) {
        if (StringUtils.isNotEmpty(mrDevice.getDeviceIds())) {
            mrDevice.setStrDeviceIds(mrDevice.getDeviceIds().split(","));
        }
        return mrDeviceMapper.selectMrDeviceList(mrDevice);
    }

    @Override
    public List<MrDevice> listDevs(MrDevice mrDevice) {
        if (StringUtils.isNotEmpty(mrDevice.getDeviceIds())) {
            mrDevice.setStrDeviceIds(mrDevice.getDeviceIds().split(","));
        }
        if (StringUtils.isNotEmpty(mrDevice.getSystemIds())) {
            mrDevice.setStrSystemIds(mrDevice.getSystemIds().split(","));
        }
        if (StringUtils.isNotEmpty(mrDevice.getDeptIds())) {
            mrDevice.setStrDepts(mrDevice.getDeptIds().split(","));
        }

        return mrDeviceMapper.listDevs(mrDevice);
    }

    @Override
    public List<TestMuteRecordVO> queryTestMuteRecord(MrDevice mrDevice) {
        if (StringUtils.isNotEmpty(mrDevice.getSystemIds())) {
            mrDevice.setStrSystemIds(mrDevice.getSystemIds().split(","));
        }
        return mrDeviceMapper.queryTestMuteRecord(mrDevice);
    }

    @Override
    public List<ByCategoryVO> byCategory(MrDevice mrDevice) {
        return mrDeviceMapper.byCategory(mrDevice);
    }

    @Override
    public List<MrDeviceVO> listByDeptIdAndModality(MrDevice mrDevice) {
        return mrDeviceMapper.listByDeptIdAndModality(mrDevice);
    }

    @Override
    public MrDevice getDeviceByOutputSource(String port) {
        return mrDeviceMapper.getDeviceByOutputSource(port);
    }

    @Override
    public void mute(boolean writeValue, String outputSource) {
        boolean flag = false;
        String host;
        int port;
        int slaveId = 1;
        int offset;
        MrDevice mr = mrDeviceMapper.getDeviceByOutputSource(outputSource);
        if (mr == null) {
            logger.error("【开关量输出失败】" + outputSource + " 无设备");
            return;
        }
        if (StringUtils.isNotEmpty(mr.getHost())) {
            host = mr.getHost();
        } else {
            logger.error("【开关量输出失败】" + outputSource + " 未配置host");
            return;
        }
        if (StringUtils.isNotEmpty(mr.getPort())) {
            port = Integer.parseInt(mr.getPort());
        } else {
            logger.error("【开关量输出失败】" + outputSource + " 未配置port");
            return;
        }
        if (StringUtils.isNotEmpty(mr.getRegisterAddress())) {
            offset = Integer.parseInt(mr.getRegisterAddress());
        } else {
            logger.error("【开关量输出失败】" + outputSource + " 未配置offset");
            return;
        }
        // 添加读写锁
        redisCache.setCacheObject(CacheConstants.DEV_WRITE_LOCK + mr.getSystemId(), true, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        if (mr.getWarnStatus().equals(DeviceWarnStatus.LINE.getValue()) && writeValue) {
            insertMrDeviceAlarmData(mr);
        }
        mr.setRemark(String.valueOf(writeValue));
        mr.setLastHeartbeatTime(new Date());
        if (writeValue) {
            mr.setWarnStatus(DeviceWarnStatus.ALARM.getValue());
        } else {
            mr.setWarnStatus(DeviceWarnStatus.LINE.getValue());
        }
        mrDeviceMapper.updateMrDevice(mr);
        try {
            String msg = "【开关量输出】入参，host:" + host + ", port:" + port + ", slaveId:" + slaveId + ", offset:" + offset + ", writeValue:" + writeValue;
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            // 指令1
            flag = Modbus4jWriteUtils.writeCoil(host, port, slaveId, offset, writeValue);
            // 更新通道状态
            sensorDataService.updateDeviceStatus(String.valueOf(port));
            if (writeValue && outputSource.equals(MaritimeConstants.BUZZER)) {
                List<SysDictData> sysDictDatas = sysDictDataMapper.selectDictDataByType(ConstantsEnum.SYS_EXT_VOICES.getValue());
                for (SysDictData sysDictData : sysDictDatas) {
                    muteBySystemId(true, sysDictData.getDictValue());
                }
            }
            // 释放读写锁
            redisCache.setCacheObject(CacheConstants.DEV_WRITE_LOCK + mr.getSystemId(), false);
        } catch (ModbusTransportException | ModbusInitException e) {
            // 通道报警
            sensorDataService.channelAlarm(port);
            String msg = "【开关量输出失败】：" + e.getMessage();
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            // 释放读写锁
            redisCache.setCacheObject(CacheConstants.DEV_WRITE_LOCK + mr.getSystemId(), false);
        }
    }

    private void insertMrDeviceAlarmData(MrDevice mr) {
        MrDeviceAlarmData mrDeviceAlarmData = new MrDeviceAlarmData();
        // 08K-014 报警
        String content2 = mr.getSystemId() + ":" + mr.getDeviceName() + " 报警" + "%%%" + mr.getDisplayAlarm();
        mrDeviceAlarmData.setAlarmTime(new Date());
        mrDeviceAlarmData.setDeviceCode(mr.getSystemId());
        mrDeviceAlarmData.setDeviceName(mr.getDeviceName());
        mrDeviceAlarmData.setContent(content2.split("%%%")[0]);
        mrDeviceAlarmData.setDisplayAlarm(mr.getDisplayAlarm());
        mrDeviceAlarmDataService.insertMrDeviceAlarmData(mrDeviceAlarmData);
    }

    @Override
    public void muteBySystemId(boolean writeValue, String systemId) {
        boolean flag = false;
        String host;
        int port;
        int slaveId = 1;
        int offset;
        MrDevice mr = mrDeviceMapper.getDeviceBySystemId(systemId);
        if (mr == null) {
            logger.error("【开关量输出失败】" + systemId + " 无设备");
            return;
        }
        if (StringUtils.isNotEmpty(mr.getHost())) {
            host = mr.getHost();
        } else {
            logger.error("【开关量输出失败】" + systemId + " 未配置host");
            return;
        }
        if (StringUtils.isNotEmpty(mr.getPort())) {
            port = Integer.parseInt(mr.getPort());
        } else {
            logger.error("【开关量输出失败】" + systemId + " 未配置port");
            return;
        }
        if (StringUtils.isNotEmpty(mr.getRegisterAddress())) {
            offset = Integer.parseInt(mr.getRegisterAddress());
        } else {
            logger.error("【开关量输出失败】" + systemId + " 未配置offset");
            return;
        }
        if (mr.getWarnStatus().equals(DeviceWarnStatus.LINE.getValue()) && writeValue) {
            insertMrDeviceAlarmData(mr);
        }
        mr.setRemark(String.valueOf(writeValue));
        mr.setLastHeartbeatTime(new Date());
        if (writeValue) {
            mr.setWarnStatus(DeviceWarnStatus.ALARM.getValue());
        } else {
            mr.setWarnStatus(DeviceWarnStatus.LINE.getValue());
        }
        mrDeviceMapper.updateMrDevice(mr);
        try {
            String msg = "【开关量输出】入参，host:" + host + ", port:" + port + ", slaveId:" + slaveId + ", offset:" + offset + ", writeValue:" + writeValue;
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            // 指令1
            flag = Modbus4jWriteUtils.writeCoil(host, port, slaveId, offset, writeValue);
            // 更新通道状态
            sensorDataService.updateDeviceStatus(String.valueOf(port));
        } catch (ModbusTransportException | ModbusInitException e) {
            // 通道报警
            sensorDataService.channelAlarm(port);
            String msg = "【开关量输出失败】：" + e.getMessage();
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        }
    }

    /**
     * 新增设备管理
     *
     * @param mrDevice 设备管理
     * @return 结果
     */
    @Override
    public int insertMrDevice(MrDevice mrDevice) {
        mrDevice.setCreateTime(DateUtils.getNowDate());
        int count = mrDeviceMapper.insertMrDevice(mrDevice);

        if (count > 0) {
            String label = sysDictDataMapper.selectDictLabel(MODALITY_LIST, mrDevice.getModality());
            mrDevice.setModality(label);
            redisCache.setCacheObject(CacheConstants.DEV_INFO_CACHE + mrDevice.getDeviceId(), mrDevice);
        }

        return count;
    }

    @Override
    public void bathUpdateMrDevice(List<MrDevice> mrDeviceList) {
        mrDeviceMapper.bathUpdateMrDevice(mrDeviceList);
    }

    @Override
    public MrDevice getDeviceBySystemId(String systemId) {
        return mrDeviceMapper.getDeviceBySystemId(systemId);
    }

    @Override
    public void printData(HttpServletResponse response, MrDevice mrDevice) {
        List<MrDevice> list = selectMrDeviceList(mrDevice);
        if (mrDevice.getModality().isEmpty()) {
            logger.error("【printData】请选择设备类型！");
        }
        String fileName = "";
        if (mrDevice.getModality().equals(ProductEnum.BA.getValue())) {
            List<MrDeviceBAVO> voList = new ArrayList<>();
            for (MrDevice device : list) {
                MrDeviceBAVO deviceBAVO = new MrDeviceBAVO();
                deviceBAVO.setSystemId(device.getSystemId());
                deviceBAVO.setDeviceName(device.getDeviceName());
                deviceBAVO.setUnit(device.getUnit());
                deviceBAVO.setRemark(device.getRemark());
                voList.add(deviceBAVO);
            }
            ExcelUtil<MrDeviceBAVO> util = new ExcelUtil<>(MrDeviceBAVO.class);
            fileName = util.creatExcel(response, voList, "设备管理数据");
        }
        if (mrDevice.getModality().equals(ProductEnum.MR.getValue())) {
            List<MrDeviceMRVO> voList = new ArrayList<>();
            for (MrDevice device : list) {
                MrDeviceMRVO deviceMRVO = new MrDeviceMRVO();
                deviceMRVO.setSystemId(device.getSystemId());
                deviceMRVO.setDeviceName(device.getDeviceName());
                deviceMRVO.setLightColor(device.getLightColor());
                voList.add(deviceMRVO);
            }
            ExcelUtil<MrDeviceMRVO> util = new ExcelUtil<>(MrDeviceMRVO.class);
            fileName = util.creatExcel(response, voList, "设备管理数据");
        }

        String pdfName = "/home/psw/" + System.currentTimeMillis() + ".pdf";
        String printerName = sysDictDataMapper.selectDictValue("printer", "printer_name");
        PrintDemo.printPdf(fileName, pdfName, printerName);
        File file = new File(fileName);
        fileDelete(file);
        File file2 = new File(pdfName);
        fileDelete(file2);
    }

    private void fileDelete(File file) {
        if (file.exists()) {
            if (file.delete()) {
                logger.error("【printData】文件删除成功！");
            } else {
                logger.error("【printData】文件删除失败！");
            }
        } else {
            logger.error("【printData】文件不存在！");
        }
    }


    /**
     * 修改设备管理
     *
     * @param mrDevice 设备管理
     * @return 结果
     */
    @Override
    public int updateMrDevice(MrDevice mrDevice) {
        mrDevice.setUpdateTime(DateUtils.getNowDate());
        int count = mrDeviceMapper.updateMrDevice(mrDevice);

        if (count > 0) {
            String label = sysDictDataMapper.selectDictLabel(MODALITY_LIST, mrDevice.getModality());
            mrDevice.setModality(label);
            redisCache.setCacheObject(CacheConstants.DEV_INFO_CACHE + mrDevice.getDeviceId(), mrDevice);
        }

        return count;
    }

    @Override
    public int updateMrDeviceWarnStatus(MrDevice mrDevice) {
        return mrDeviceMapper.updateMrDeviceWarnStatus(mrDevice);
    }

    /**
     * 批量删除设备管理
     *
     * @param deviceIds 需要删除的设备管理主键
     * @return 结果
     */
    @Override
    public int deleteMrDeviceByDeviceIds(Long[] deviceIds) {
        int count = mrDeviceMapper.deleteMrDeviceByDeviceIds(deviceIds);

        if (count > 0) {
            try {
                for (Long deviceId : deviceIds) {
                    String devId = redisCache.getCacheObject(MyRedis.makeDeviceMappingKey(deviceId));
                    redisCache.deleteObject(MyRedis.makeDeviceKey(devId));
                    redisCache.deleteObject(MyRedis.makeDeviceMappingKey(deviceId));
                    redisCache.deleteObject(CacheConstants.DEV_INFO_CACHE + deviceId);
                }
            } catch (Exception ignored) {
            }
        }

        return count;
    }

    /**
     * 删除设备管理信息
     *
     * @param deviceId 设备管理主键
     * @return 结果
     */
    @Override
    public int deleteMrDeviceByDeviceId(Long deviceId) {
        int count = mrDeviceMapper.deleteMrDeviceByDeviceId(deviceId);

        if (count > 0) {
            try {
                String devId = redisCache.getCacheObject(MyRedis.makeDeviceMappingKey(deviceId));
                redisCache.deleteObject(MyRedis.makeDeviceKey(devId));
                redisCache.deleteObject(MyRedis.makeDeviceMappingKey(deviceId));
                redisCache.deleteObject(CacheConstants.DEV_INFO_CACHE + deviceId);
            } catch (Exception ignored) {
            }
        }

        return count;
    }

    @Override
    public int updateHeartBeat(Long deviceId, String time) {
        return mrDeviceMapper.updateHeartBeat(deviceId, time);
    }

    @Override
    public MrDevice getCacheDevice(String devId) {
        return redisCache.getCacheObject(MyRedis.makeDeviceKey(devId));
    }


    @Override
    public List<OverviewReponse> overview(MrDevice mrDevice) {
        return mrDeviceMapper.overview(mrDevice);
    }

    @Override
    public String importDevice(List<MrDevice> deviceList, boolean updateSupport, String openName) {
        if (StringUtils.isNull(deviceList) || deviceList.isEmpty()) {
            throw new ServiceException("导入设备数据不能为空！");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        for (MrDevice device : deviceList) {
            try {
                // 验证是否存在这个设备
                MrDevice d = mrDeviceMapper.getDeviceBySystemId(device.getSystemId());
                if (StringUtils.isNull(d)) {
                    BeanValidators.validateWithException(validator, device);
                    device.setCreateBy(openName);
                    this.insertMrDevice(device);
                    successNum++;
                    successMsg.append("<br/>").append(successNum).append("、设备 ").append(device.getSystemId()).append(" 导入成功");
                } else if (updateSupport) {
                    BeanValidators.validateWithException(validator, device);
                    device.setUpdateBy(openName);
                    this.updateMrDevice(device);
                    successNum++;
                    successMsg.append("<br/>").append(successNum).append("、设备 ").append(device.getSystemId()).append(" 更新成功");
                } else {
                    failureNum++;
                    failureMsg.append("<br/>").append(failureNum).append("、设备 ").append(device.getSystemId()).append(" 已存在");
                }
            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、设备 " + device.getSystemId() + " 导入失败：";
                failureMsg.append(msg).append(e.getMessage());
                logger.error(msg, e);
            }
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new ServiceException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条，数据如下：");
        }
        return successMsg.toString();
    }
}
